package org.liquidByte.simpleChat.guiClient;
import javax.swing.JDesktopPane;
import javax.swing.JTextField;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.UnknownHostException;

import javax.swing.JTextArea;
import javax.swing.JButton;

import java.text.SimpleDateFormat;
import java.util.HashMap;

import org.liquidByte.simpleChat.communication.Channel;
import org.liquidByte.simpleChat.communication.ChannelFaultedEventListener;
import org.liquidByte.simpleChat.communication.CommunicationException;
import org.liquidByte.simpleChat.dto.Message;
import org.liquidByte.simpleChat.dto.User;
import org.liquidByte.simpleChat.dto.UserIdentification;
import org.liquidByte.simpleChat.dto.UserMessage;

import javax.swing.DefaultListModel;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JList;

public class ClientGuiPanel extends JDesktopPane implements UserOfflineEventListener, UserOnlineEventListener, MessageReceivedEventListener, StatusChangedEventListener, ChannelFaultedEventListener {

	private static final long serialVersionUID = 1L;
	private JButton jButton = null;
	private JLabel jLabel = null;
	private JLabel jLabel1 = null;
	private JTextField jTextField2 = null;
	private HashMap<UserIdentification, User> userOnline;
	private JScrollPane jOnlineUsers = null;
	private JList jOnlineUserList = null;
	private JTextArea jMessages = null;
	private JLabel jlblIP = null;
	private JTextField jtxtIP = null;
	private JLabel jlblPontos = null;
	private JTextField jtxtPorta = null;
	private JButton jbtnConnect = null;
	private JLabel jlblNick = null;
	private JTextField jtxtNick = null;
	/**
	 * This is the default constructor
	 */
	public ClientGuiPanel() {
		super();
		this.initialize();
		this.initializeEvents();
		this.updateTo(false);
	}
	
	private HashMap<UserIdentification, User> getUserOnline() {
		if (this.userOnline == null) {
			this.userOnline = new HashMap<UserIdentification, User>();
		}
		return this.userOnline;
	}

	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	private void initialize() {
		jlblNick = new JLabel();
		jlblNick.setBounds(new Rectangle(331, 11, 32, 16));
		jlblNick.setText("Nick");
		jlblPontos = new JLabel();
		jlblPontos.setBounds(new Rectangle(226, 10, 45, 16));
		jlblPontos.setText(": Porta");
		jlblIP = new JLabel();
		jlblIP.setBounds(new Rectangle(10, 12, 25, 16));
		jlblIP.setText("IP");
		jLabel1 = new JLabel();
		jLabel1.setBounds(new Rectangle(12, 45, 34, 16));
		jLabel1.setText("Chat");
		jLabel = new JLabel();
		jLabel.setBounds(new Rectangle(500, 41, 60, 16));
		jLabel.setText("Onlines");
		this.setSize(628, 269);

		this.add(getJButton(), null);
		this.add(jLabel, null);
		this.add(jLabel1, null);
		this.add(getJTextField2(), null);
		this.add(getJOnlineUsers(), null);
		this.add(getJMessages(), null);
		this.add(jlblIP, null);
		this.add(getJtxtIP(), null);
		this.add(jlblPontos, null);
		this.add(getJtxtPorta(), null);
		this.add(getJbtnConnect(), null);
		this.add(jlblNick, null);
		this.add(getJtxtNick(), null);
	}
	
	private void initializeEvents() {
		EventService.addMessageListener(this);
		EventService.addOfflineListener(this);
		EventService.addOnlineListener(this);
		EventService.addStatusListener(this);
	}

	/**
	 * This method initializes jButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getJButton() {
		class ThisActionListener implements ActionListener {
			private ClientGuiPanel panel;
			public ThisActionListener(ClientGuiPanel panel) {
				this.panel = panel;
			}

			@Override
			public void actionPerformed(ActionEvent e) {
				this.panel.sendMessage();
			}			
		}
		if (jButton == null) {
			jButton = new JButton();
			jButton.setBounds(new Rectangle(498, 209, 120, 52));
			jButton.setText("Enviar");
			jButton.addActionListener(new ThisActionListener(this));
		}
		return jButton;
	}
	private void sendMessage() {
		Message msg = new Message(this.jTextField2.getText());
		try {
			ClientController.getCurrent().getChatClient().sendMessage(msg);
			this.jTextField2.setText(null);
		} catch (IOException ex) {
			this.showError(ex);
		} catch (CommunicationException ex) {
			this.showError(ex);
		}
	}
	private void showError(Throwable ex) {
		this.jMessages.append("\r\n");
		this.jMessages.append(this.getSimpleDateTimeString());
		this.jMessages.append(" - Error");
		this.jMessages.append("\r\n");
		this.jMessages.append(ClientGuiPanel.formatThrowable(ex));
		if(ex instanceof IOException){
			JOptionPane.showMessageDialog(null, "IOException");	
		}
		else if(ex instanceof CommunicationException){
			JOptionPane.showMessageDialog(null, "CommunicationException");
		}else if(ex instanceof UnknownHostException){
			JOptionPane.showMessageDialog(null, "UnknowHostException");
		}else if(ex instanceof NumberFormatException){
			JOptionPane.showMessageDialog(null, "NumberFormatException");
		}
	}
	private static String formatThrowable(Throwable trw) {
		
		StringBuffer sb = new StringBuffer();
		Throwable current = trw;
		
		while (current != null) {
			sb.append("message ");
			sb.append(current.getMessage());
			sb.append("\r\n");
			if (current.getStackTrace().length > 0) {
				sb.append("stack trace:");
				sb.append("\r\n");
				for (StackTraceElement element : current.getStackTrace()) {
					sb.append(element.getClassName());
					sb.append(".");
					
					sb.append(" - ");
					sb.append(element.getClassName());
					sb.append(":");
					sb.append(element.getLineNumber());
					sb.append("\r\n");
				}
				if (current.getCause() != null) {
					sb.append("caused by");
					sb.append("\r\n");
				}
			}
		}
		
		return sb.toString();
	}

	/**
	 * This method initializes jTextField2	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getJTextField2() {
		if (jTextField2 == null) {
			jTextField2 = new JTextField();
			jTextField2.setBounds(new Rectangle(11, 210, 483, 52));
			
		}
		return jTextField2;
	}

	@Override
	public void onUserOffline(UserIdentification userId) {
		this.getUserOnline().remove(userId);
	}
	@Override
	public void onUserOnline(User user) {
		this.getUserOnline().put(user.getUserIdent(), user);
		this.updateUserOnlineList();
	}
	@Override
	public void onMessageReceived(UserMessage userMsg) {
		this.jMessages.append("\r\n");
		this.jMessages.append(this.getSimpleDateTimeString());
		this.jMessages.append(" - ");
		User user = this.getUserOnline().get(userMsg.getUserInfo());
		this.jMessages.append(user.getName());
		this.jMessages.append(" disse ");
		this.jMessages.append(userMsg.getDescription());
	}
	@Override
	public void onStatusChanged(User user) {
		this.getUserOnline().remove(user.getUserIdent());
		this.getUserOnline().put(user.getUserIdent(), user);
		this.updateUserOnlineList();
	}
	private void updateUserOnlineList() {
		DefaultListModel listModel = new DefaultListModel();
		for (User user : this.getUserOnline().values()) {
			listModel.addElement(user.getName()); // + "(" + user.getStatus() + "): " + user.getStatusMessage());
		}		
		this.jOnlineUserList.setModel(listModel);
	}
	private String getSimpleDateTimeString() {
		return new SimpleDateFormat("hh:mm:ss").format(System.currentTimeMillis());
	}
 
	private void updateTo(boolean connected) {
		if (connected) {
			this.jbtnConnect.setText("Desconectar");	
		}
		else {
			this.jbtnConnect.setText("Conectar");
		}
		
		this.jtxtPorta.setEnabled(!connected);
		this.jtxtIP.setEnabled(!connected);
		this.jtxtNick.setEnabled(!connected);
		
		this.jTextField2.setEnabled(connected);
		this.jButton.setEnabled(connected);
	}
	private User getUser() {
		User user = new User(this.jtxtNick.getText(), "N/A", "N/A");
		return user;
	}
	private void refreshOnlineList() {
		this.getUserOnline().clear();
		
		try {
			for (User user : ClientController.getCurrent().getChatClient().getOnlineList()) {
				this.getUserOnline().put(user.getUserIdent(), user);
			}
		} catch (IOException e) {
			this.showError(e);
		} catch (CommunicationException e) {
			this.showError(e);
		}
	}
	private void tryConnect() {
		String strIp = this.jtxtIP.getText();
		String strPort = this.jtxtPorta.getText(); 
		if (strIp == null || strIp == "") {
			JOptionPane.showMessageDialog(null, "Algum ip ou host deve ser digitado!");
		}
		else if (strPort == null || strPort == "") {
			JOptionPane.showMessageDialog(null, "Alguma porta deve ser informada");
		}
		else {
			try {
				int port = Integer.parseInt(strPort);
				ClientController.connect(strIp, port);
				this.updateTo(true);
				ClientController.getCurrent().getChatClient().login(this.getUser());
				
				this.refreshOnlineList();
				this.updateUserOnlineList();
			}
			catch (CommunicationException e) {
				this.showError(e);
				this.updateTo(false);
			}
			catch (NumberFormatException e) {
				this.showError(e);
				this.updateTo(false);
			}catch (UnknownHostException e) {
				this.showError(e);
				this.updateTo(false);
			} catch (IOException e) {
				this.showError(e);
				this.updateTo(false);
			}
		}
	}
	
	/**
	 * This method initializes jOnlineUsers	
	 * 	
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getJOnlineUsers() {
		if (jOnlineUsers == null) {
			jOnlineUsers = new JScrollPane();
			jOnlineUsers.setBounds(new Rectangle(499, 66, 117, 133));
			jOnlineUsers.setViewportView(getJOnlineUserList());
		}
		return jOnlineUsers;
	}

	/**
	 * This method initializes jOnlineUserList	
	 * 	
	 * @return javax.swing.JList	
	 */
	private JList getJOnlineUserList() {
		if (jOnlineUserList == null) {
			jOnlineUserList = new JList();
		}
		return jOnlineUserList;
	}

	/**
	 * This method initializes jMessages	
	 * 	
	 * @return javax.swing.JTextArea	
	 */
	private JTextArea getJMessages() {
		if (jMessages == null) {
			jMessages = new JTextArea();
			jMessages.setBounds(new Rectangle(12, 67, 477, 128));
			jMessages.setEditable(false);
		}
		return jMessages;
	}

	/**
	 * This method initializes jtxtIP	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getJtxtIP() {
		if (jtxtIP == null) {
			jtxtIP = new JTextField();
			jtxtIP.setBounds(new Rectangle(49, 9, 170, 20));
		}
		return jtxtIP;
	}

	/**
	 * This method initializes jtxtPorta	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getJtxtPorta() {
		if (jtxtPorta == null) {
			jtxtPorta = new JTextField();
			jtxtPorta.setBounds(new Rectangle(275, 8, 50, 20));
		}
		return jtxtPorta;
	}

	/**
	 * This method initializes jbtnConnect	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getJbtnConnect() {
		class ThisActionListener implements ActionListener {
			private ClientGuiPanel panel;
			public ThisActionListener(ClientGuiPanel panel) {
				this.panel = panel;
			}
			
			@Override
			public void actionPerformed(ActionEvent arg0) {
				this.panel.tryConnect();
			}
		}
		if (jbtnConnect == null) {
			jbtnConnect = new JButton();
			jbtnConnect.setBounds(new Rectangle(515, 10, 104, 18));
			jbtnConnect.setText("Conectar");
			jbtnConnect.addActionListener(new ThisActionListener(this));
		}
		return jbtnConnect;
	}

	/**
	 * This method initializes jtxtNick	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getJtxtNick() {
		if (jtxtNick == null) {
			jtxtNick = new JTextField();
			jtxtNick.setBounds(new Rectangle(368, 9, 139, 20));
		}
		return jtxtNick;
	}

	public static void main(String[] args) {
		ClientGuiPanel panel = new ClientGuiPanel();
		panel.setVisible(true);
	}

	@Override
	public void faulted(Channel channel, Throwable cause) {
		this.showError(cause);
		this.updateTo(false);
	}
}  //  @jve:decl-index=0:visual-constraint="25,12"
